Neste tutorial abordarei maneiras de criptografar senhas para serem usadas em sistemas de login. Também mostrarei uma possível forma de salvá-las em cookies, a fim de viabilizar login automático, porém sem salvar a senha ou o hash dela no cookie.
Na verdade, “criptografia” não é a palavra correta aqui. Na verdade usaremos hashes, que é uma forma de codificação unidirecional, ou seja, não há como, a partir de um hash, obter o dado original. Afinal, não temos para que saber a senha do usuário. Criptografia é uma forma de codificação usando chaves, de forma que é possível obter o dado original, desde que tenha-se a chave com a qual o dado foi codificado.
Sumário
Codificando senhas para salvá-las no banco de dados
Usando Salt Number
Salvando a senha em cookie de forma segura
Conclusão
Codificando senhas para salvá-las no banco de dados
Nos exemplos aqui mostrados usarei a linguagem PHP, mas os conceitos podem ser usados com qualquer linguagem de programação.
Por incrível que possa parecer, há muitas pessoas que salvam senhas puras no banco de dados, sem qualquer tipo de criptografia! Isso é um crime! Se alguém tiver acesso ao banco de dados, todos os usuários do sistemas estarão em risco. As senhas devem ser salvas criptografadas ou, como no nosso caso, em hashes.
Normalmente usa-se o MD5, que gera uma saída de 32 caracteres. Porém, toda segurança é pouca. Temos o SHA-1, que gera uma saída de 40 caracteres. A fim de evitar colisões, devemos dar preferência ao SHA-1, uma vez que ele gera mais possibilidades de saídas.
Mas também podemos unir as duas! Uma possibilidade é esta:
function hashPasswd( $passwd ) { return sha1( md5( $passwd ) ); } |
A função codifica com MD5 e depois codifica o resultado do MD5 com SHA-1. É uma segurança a mais, já que uma dicionário de senhas codificadas em SHA-1 provavelmente não contém hashes MD5 de senhas para serem testados.
Para ficar ainda mais seguro, você pode usar uma “chave” – não exatamente com o significado de uma chave de criptografia. Na verdade é uma string que concatenamos com a senha, a fim de gerar uma senha mais complexa:
define( 'HASH_KEY', md5( 'chave secreta' ) ); function hashPasswdWithKey( $passwd ) { return sha1( md5( $passwd . HASH_KEY ) ); } |
HASH_KEY é uma constante com o hash MD5 de uma string. Prefira usar uma string relativamente complexa, com números e caracteres maiúsculos e minúsculos no lugar de “chave secreta”.
O hash retornado é o valor que deve ser salvo no banco de dados.
Quando o usuário efetuar login, com nome de usuário e senha, basta pegarmos a senha informada, codificá-la com a nossa função e compará-la com o valor salvo no banco de dados.
Usando Salt Number
O uso de Salt Number dificulta a realização de ataques por brute force, uma vez que aumenta exponencialmente as possibilidades de hash para uma mesma palavra.
Os links abaixo explicam detalhadamente como funcionam os salt numbers:
http://www.vivaolinux.com.br/artigo/Armazenamento-de-senhas-no-Linux/?pagina=7
http://gravatai.ulbra.tche.br/~elgio/senhas.html
O Salt Numbeer é semelhante à HASH_KEY, que criamos anteriormente. Porém, em vez de usarmos uma constante, criamos um código aleatório para cada usuário. Esse valor fica salvo no banco de dados e é concatenado à senha do usuário, da mesma forma que fizemos com a constante HASH_KEY.
Uma possível forma de gerar códigos aleatórios para o Salt Number em PHP é esta:
// Número de caracteres do Salt Number define( 'SALT_SIZE', 15 ); // Array com os 62 caracteres (a-z, A-Z, 0-9) $amostra = array_merge( range( 'a', 'z' ), range( 'A', 'Z' ), range( 0, 9 ) ); // Embaralha o array shuffle( $amostra ); $index = array_rand( $amostra, SALT_SIZE ); $salt = ''; for ( $i = 0; $i < SALT_SIZE; $i++ ) { $salt .= $amostra[ $i ]; } |
Assim, $salt contém o salt number gerado, para que seja usado na autenticação de um novo usuário recém-cadastrado.
Salvando a senha em cookie de forma segura
A fim de permitirmos o login automático, muito comum em fóruns e lojas virtuais, precisamos usar cookies. Neles devem estar salvos os dados do usuário para autenticação no sistema.
Porém, não podemos colocar a senha do usuário no cookie. Mesmo que seja o hash da senha, pois fornecer acesso à hash salva no banco de dados é uma falha de segurança, já que ter um hash em mãos torna muito mais fácil um ataque por brute force. Por isso, vamos criar outro hash, baseado no hash da senha e no horário em que o usuário fez o último login.
define( 'HASH_KEY', md5( 'chave secreta' ) ); define( 'HASH_PATTERN', HASH_KEY . '%d' . '%s' ); function hashForCookie( $str ) { $strHash = sprintf( HASH_PATTERN, time(), $str ); return sha1( $strHash ); } |
Novamente temos a HASH_KEY, utilizada anteriormente. Criamos outra constante, a HASH_PATTERN, que é o padrão que será usado no hash. Aqui uso a chave (HASH_KEY), à qual são concatenados um “%d” e um “%s”, que serão substituídos pelo timestamp corrente e pelo hash da senha, respectivamente, usando sprintf(). A função retorna o SHA-1 da string final.
Essa função deve ser usada quando o usuário efetuar login. Assim que ele logar, salve no banco de dados (num campo diferente do da senha, claro) o hash gerado por hashForCookie. Esse é o dado que deverá ser salvo no cookie, além do nome do usuário, evitando salvar o hash da senha na máquina no usuário.
Para autenticar, verifique se o hash lido do cookie é o mesmo do salvo no banco de dados. Se for, autenticado, senão remova o cookie e solicite nome de usuário e senha. Assim que fizer login, gere o novo hash para o cookie, salvando-o no cookie, como citado anteriormente.
Uma forma simples de salvar esses dados num cookie usando PHP:
$user = 'Beraldo'; $hash = '2f9a922f0401476cd7089c235d76de5bb61150b9'; $dados = array( 'user' => $user, 'passwd' => $hash ); setcookie( 'nome_do_cookie', serialize( $dados ), time() + 86400 ); |
A função serialize serializa um objeto, retornando uma representação em string do mesmo. O caminho oposto pode ser feito com unserialize:
$cookie = $_COOKIE['nome_do_cookie']; $dados = unserialize( $cookie ); |
Assim, $dados é o array que definimos no código anterior.
Conclusão
Essas não são soluções finais para os dois casos apresentados. São apenas sugestões. É possível criar suas próprias funções, próprias criptografias etc.
O importante é procurar formas seguras de desenvolver sistemas.
Roberto Beraldo
Latest posts by Roberto Beraldo (see all)
- Não Tenha Preguiça de Ler! - 25/04/2016
- Como Atualizar Scripts PHP de MySQL Para MySQLi - 29/10/2015
- Como usar PDO com banco de dados MySQL - 10/09/2015